Produce artifacts JSON messages even for fresh builds
authorAleksey Kladov <aleksey.kladov@gmail.com>
Wed, 22 Feb 2017 08:36:44 +0000 (11:36 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Sat, 25 Feb 2017 12:20:06 +0000 (15:20 +0300)
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/machine_message.rs
tests/build.rs

index 3e58b1a6a2fc5da6ba6c6174815a260fea6fa7d7..620ff267f2e9d6eef3884bb784655c14c53c9ad2 100644 (file)
@@ -230,11 +230,9 @@ fn compile<'a, 'cfg: 'a>(cx: &mut Context<'a, 'cfg>,
         } else {
             rustc(cx, unit, exec.clone())?
         };
-        let link_work1 = link_targets(cx, unit)?;
-        let link_work2 = link_targets(cx, unit)?;
         // Need to link targets on both the dirty and fresh
-        let dirty = work.then(link_work1).then(dirty);
-        let fresh = link_work2.then(fresh);
+        let dirty = work.then(link_targets(cx, unit, false)?).then(dirty);
+        let fresh = link_targets(cx, unit, true)?.then(fresh);
         (dirty, fresh, freshness)
     };
     jobs.enqueue(cx, unit, Job::new(dirty, fresh), freshness)?;
@@ -291,10 +289,6 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
     let json_messages = cx.build_config.json_messages;
     let package_id = unit.pkg.package_id().clone();
     let target = unit.target.clone();
-    let profile = unit.profile.clone();
-    let features = cx.resolve.features(unit.pkg.package_id()).iter()
-                     .map(|s| s.to_owned())
-                     .collect();
 
     exec.init(cx);
     let exec = exec.clone();
@@ -388,18 +382,6 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
             fingerprint::append_current_dir(&dep_info_loc, &cwd)?;
         }
 
-        if json_messages {
-            machine_message::emit(machine_message::Artifact {
-                package_id: &package_id,
-                target: &target,
-                profile: &profile,
-                features: features,
-                filenames: filenames.iter().map(|&(ref src, _, _)| {
-                    src.display().to_string()
-                }).collect(),
-            });
-        }
-
         Ok(())
     }));
 
@@ -435,21 +417,36 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc<Executor>) -> CargoResult<Work
 
 /// Link the compiled target (often of form foo-{metadata_hash}) to the
 /// final target. This must happen during both "Fresh" and "Compile"
-fn link_targets(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
+fn link_targets(cx: &mut Context, unit: &Unit, fresh: bool) -> CargoResult<Work> {
     let filenames = cx.target_filenames(unit)?;
+    let package_id = unit.pkg.package_id().clone();
+    let target = unit.target.clone();
+    let profile = unit.profile.clone();
+    let features = cx.resolve.features_sorted(&package_id).into_iter()
+        .map(|s| s.to_owned())
+        .collect();
+    let json_messages = cx.build_config.json_messages;
+
     Ok(Work::new(move |_| {
         // If we're a "root crate", e.g. the target of this compilation, then we
         // hard link our outputs out of the `deps` directory into the directory
         // above. This means that `cargo build` will produce binaries in
         // `target/debug` which one probably expects.
-        for (src, link_dst, _linkable) in filenames {
+        let mut destinations = vec![];
+        for &(ref src, ref link_dst, _linkable) in filenames.iter() {
             // This may have been a `cargo rustc` command which changes the
             // output, so the source may not actually exist.
-            debug!("Thinking about linking {} to {:?}", src.display(), link_dst);
-            if !src.exists() || link_dst.is_none() {
+            if !src.exists() {
                 continue
             }
-            let dst = link_dst.unwrap();
+            let dst = match link_dst.as_ref() {
+                Some(dst) => dst,
+                None => {
+                    destinations.push(src.display().to_string());
+                    continue;
+                }
+            };
+            destinations.push(dst.display().to_string());
 
             debug!("linking {} to {}", src.display(), dst.display());
             if dst.exists() {
@@ -457,16 +454,27 @@ fn link_targets(cx: &mut Context, unit: &Unit) -> CargoResult<Work> {
                     human(format!("failed to remove: {}", dst.display()))
                 })?;
             }
-            fs::hard_link(&src, &dst)
+            fs::hard_link(src, dst)
                  .or_else(|err| {
                      debug!("hard link failed {}. falling back to fs::copy", err);
-                     fs::copy(&src, &dst).map(|_| ())
+                     fs::copy(src, dst).map(|_| ())
                  })
                  .chain_error(|| {
                      human(format!("failed to link or copy `{}` to `{}`",
                                    src.display(), dst.display()))
             })?;
         }
+
+        if json_messages {
+            machine_message::emit(machine_message::Artifact {
+                package_id: &package_id,
+                target: &target,
+                profile: &profile,
+                features: features,
+                filenames: destinations,
+                fresh: fresh,
+            });
+        }
         Ok(())
     }))
 }
index 9626494cd3d1c6b6a4415939f478071f18021ef1..1d4f33a86da9bbfdac131b0a1be3d24ec9008fcb 100644 (file)
@@ -33,6 +33,7 @@ pub struct Artifact<'a> {
     pub profile: &'a Profile,
     pub features: Vec<String>,
     pub filenames: Vec<String>,
+    pub fresh: bool,
 }
 
 impl<'a> Message for Artifact<'a> {
index c937f0487e455418ef367c13fff86bc999f2d55a..433e2ed51e02d34f0d2e416daaae5b16c76b1694 100644 (file)
@@ -2512,8 +2512,9 @@ fn compiler_json_error_format() {
             authors = ["wycats@example.com"]
         "#)
         .file("bar/src/lib.rs", r#"fn dead() {}"#);
+    p.build();
 
-    assert_that(p.cargo_process("build").arg("-v")
+    assert_that(p.cargo("build").arg("-v")
                     .arg("--message-format").arg("json"),
                 execs().with_status(0).with_json(r#"
     {
@@ -2544,7 +2545,8 @@ fn compiler_json_error_format() {
             "name":"bar",
             "src_path":"[..]lib.rs"
         },
-        "filenames":["[..].rlib"]
+        "filenames":["[..].rlib"],
+        "fresh": false
     }
 
     {
@@ -2575,7 +2577,54 @@ fn compiler_json_error_format() {
             "test": false
         },
         "features": [],
-        "filenames": ["[..]"]
+        "filenames": ["[..]"],
+        "fresh": false
+    }
+"#));
+
+    // With fresh build, we should repeat the artifacts,
+    // but omit compiler warnings.
+    assert_that(p.cargo("build").arg("-v")
+                    .arg("--message-format").arg("json"),
+                execs().with_status(0).with_json(r#"
+    {
+        "reason":"compiler-artifact",
+        "profile": {
+            "debug_assertions": true,
+            "debuginfo": 2,
+            "opt_level": "0",
+            "test": false
+        },
+        "features": [],
+        "package_id":"bar 0.5.0 ([..])",
+        "target":{
+            "kind":["lib"],
+            "crate_types":["lib"],
+            "name":"bar",
+            "src_path":"[..]lib.rs"
+        },
+        "filenames":["[..].rlib"],
+        "fresh": true
+    }
+
+    {
+        "reason":"compiler-artifact",
+        "package_id":"foo 0.5.0 ([..])",
+        "target":{
+            "kind":["bin"],
+            "crate_types":["bin"],
+            "name":"foo",
+            "src_path":"[..]main.rs"
+        },
+        "profile": {
+            "debug_assertions": true,
+            "debuginfo": 2,
+            "opt_level": "0",
+            "test": false
+        },
+        "features": [],
+        "filenames": ["[..]"],
+        "fresh": true
     }
 "#));
 }
@@ -2633,7 +2682,8 @@ fn message_format_json_forward_stderr() {
             "test":false
         },
         "features":[],
-        "filenames":["[..]"]
+        "filenames":[],
+        "fresh": false
     }
 "#));
 }